package br.com.hs.nfe;

import br.com.hs.nfe.common.exception.HSCommonException;
import br.com.hs.nfe.common.to.CofinsSTTO;
import br.com.hs.nfe.common.to.CofinsTO;
import br.com.hs.nfe.common.to.IITO;
import br.com.hs.nfe.common.to.IcmsTO;
import br.com.hs.nfe.common.to.IpiTO;
import br.com.hs.nfe.common.to.IssqnTO;
import br.com.hs.nfe.common.to.NotaFiscalTO;
import br.com.hs.nfe.common.to.PisSTTO;
import br.com.hs.nfe.common.to.PisTO;
import br.com.hs.nfe.common.to.ProdutoNotaTO;
import br.com.hs.nfe.common.to.ProdutoTO;
import br.com.hs.nfe.common.enumeration.IndTotEnum;
import br.com.hs.nfe.common.util.DocumentHelper;
import br.com.hs.nfe.common.util.HSConstants;
import br.com.hs.nfe.common.util.NumberHelper;
import br.com.hs.nfe.common.util.StringHelper;
import br.inf.portalfiscal.nfe.NFeDocument;
import br.inf.portalfiscal.nfe.TNFe;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;

/**
 * Classe responsável por auxiliar nas operações com a NF-e.
 * @author Ranlive Hrysyk
 */
public class NotaFiscalHelper {
    
    private BigDecimal vBCICMS;
    private BigDecimal vICMS;
    private BigDecimal vICMSBCST;
    private BigDecimal vICMSST;
    private BigDecimal vProd;
    private BigDecimal vFrete;
    private BigDecimal vSeg;
    private BigDecimal vDesc;
    private BigDecimal vII;
    private BigDecimal vIPI;
    private BigDecimal vPIS;
    private BigDecimal vCOFINS;
    private BigDecimal vOutro;
    private BigDecimal vBCISSQN;
    private BigDecimal vISSQN;
    private NumberHelper numberHelper;

    /**
     * Construtor padrão da classe.
     */
    public NotaFiscalHelper() {
        numberHelper = new NumberHelper(NumberHelper.PreDefinedNumberFormatEnum.EN_US);
    }

    /**
     * Inicializa as variáveis dos totais.
     */
    private void initTotais() {
        vBCICMS = BigDecimal.ZERO;
        vICMS = BigDecimal.ZERO;
        vICMSBCST = BigDecimal.ZERO;
        vICMSST = BigDecimal.ZERO;
        vProd = BigDecimal.ZERO;
        vFrete = BigDecimal.ZERO;
        vSeg = BigDecimal.ZERO;
        vDesc = BigDecimal.ZERO;
        vII = BigDecimal.ZERO;
        vIPI = BigDecimal.ZERO;
        vPIS = BigDecimal.ZERO;
        vCOFINS = BigDecimal.ZERO;
        vOutro = BigDecimal.ZERO;
        vBCISSQN = BigDecimal.ZERO;
        vISSQN = BigDecimal.ZERO;
    }

    /**
     * Efetua o cálculo dos valores dos impostos dos itens da NF-e.
     */
    public void calculateImpostos(NotaFiscalTO notaFiscalTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscalTO.getListaProdutos()) {
            calculateVICMS(produtoNotaTO.getICMS());
            calculateVICMSST(produtoNotaTO.getICMS());
            calculateVPIS(produtoNotaTO.getPIS());
            calculateVPISST(produtoNotaTO.getPISST());
            calculateVCOFINS(produtoNotaTO.getCOFINS());
            calculateVCOFINSST(produtoNotaTO.getCOFINSST());
            calculateVIPI(produtoNotaTO.getIPI());
        }
    }

    /**
     * Calcula o valor do ICMS e/ou do ICMSST para os items da NF-e.
     * <p>Para cálculo do "<b>vICMS</b>" para os tipos: ICMS00, ICMS10, ICMS20, ICMS51, ICMS70,
     * ICMS90, ICMSPart e ICMSSN900 a fórmula de cálculo é:
     * <br><blockquote><i><b>vICMS = vBC x (pICMS / 100)</b></i></blockquote>
     * <p>Para cálculo do "<b>vICMSST</b>" para os tipos: ICMS10, ICMS70, ICMS90, ICMSPart e ICMSSN900 a fórmule é:
     * <br><blockquote><i><b>vICMSST = (vBCST x (pICMSST / 100)) - vICMS</b></i></blockquote>
     * <br>E para os tipos: ICMS30, ICMS201 e ICMS202 a fórmula é:
     * <br><blockquote><i><b>vICMSST = vBCST x (pICMSST / 100)</b></i></blockquote>
     */
    public void calculateVICMS(NotaFiscalTO notaFiscaTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscaTO.getListaProdutos()) {
            calculateVICMS(produtoNotaTO.getICMS());
        }
    }

    /**
     * Calcula o valor do ICMS para os items da NF-e.
     * <p>Para cálculo do "<b>vICMS</b>" para os tipos: ICMS00, ICMS10, ICMS20, ICMS51, ICMS70,
     * ICMS90, ICMSPart e ICMSSN900 a fórmula de cálculo é:
     * <br><blockquote><i><b>vICMS = vBC x (pICMS / 100)</b></i></blockquote>
     */
    public void calculateVICMS(IcmsTO icmsTO) {
        if (icmsTO != null) {
            String valorICMS = null;
            if (icmsTO.getCSTEnum() != null) {
                //Calcula o ICMS                
                switch (icmsTO.getCSTEnum().ordinal()) {
                    case 0: //ICMS00
                    case 1: //ICMS10
                    case 2: //ICMS20
                    case 7: //ICMS51
                    case 9: //ICMS70
                    case 10: //ICMS90
                    case 11: //ICMSPart 10
                    case 12: //ICMSPart 90
                        valorICMS = numberHelper.multiplicar(icmsTO.getVBC().toString(), numberHelper.getPercentual(icmsTO.getPICMS().toString()));
                        break;
                }
            } else if (icmsTO.getCSOSNEnum() != null) {
                //Calcula o ICMS
                switch (icmsTO.getCSOSNEnum().ordinal()) {
                    case 9: //ICMSSN900
                        valorICMS = numberHelper.multiplicar(icmsTO.getVBC().toString(), numberHelper.getPercentual(icmsTO.getPICMS().toString()));
                        break;
                }
            }
            icmsTO.setVICMS(numberHelper.getBigDecimal(valorICMS));
        }
    }
    
    public void calculateVICMSST(NotaFiscalTO notaFiscalTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscalTO.getListaProdutos()) {
            calculateVICMSST(produtoNotaTO.getICMS());
        }
    }

    /**
     * Calcula o valor do ICMS ST para os items da NF-e.
     * <p>Para cálculo do "<b>vICMSST</b>" para os tipos: ICMS10, ICMS70, ICMS90, ICMSPart e ICMSSN900 a fórmule é:
     * <br><blockquote><i><b>vICMSST = (vBCST x (pICMSST / 100)) - vICMS</b></i></blockquote>
     * <br>E para os tipos: ICMS30, ICMS201 e ICMS202 a fórmula é:
     * <br><blockquote><i><b>vICMSST = vBCST x (pICMSST / 100)</b></i></blockquote>
     */
    public void calculateVICMSST(IcmsTO icmsTO) {
        if (icmsTO != null) {
            String valorICMSST = null;
            if (icmsTO.getCSTEnum() != null) {
                //Calcula o ICMSST                
                switch (icmsTO.getCSTEnum().ordinal()) {
                    case 1: //ICMS10
                    case 9: //ICMS70
                    case 10: //ICMS90
                    case 11: //ICMSPart 10
                    case 12: //ICMSPart 90
                        valorICMSST = calculeVICMSST(icmsTO.getVBCST().toString(), icmsTO.getPICMSST().toString(), icmsTO.getVICMS().toString());
                        break;
                    case 3: //ICMS30
                        valorICMSST = calculeVICMSST(icmsTO.getVBCST().toString(), icmsTO.getPICMSST().toString(), null);
                        break;
                }
            } else if (icmsTO.getCSOSNEnum() != null) {
                //Calcula o ICMSST
                switch (icmsTO.getCSOSNEnum().ordinal()) {
                    case 9: //ICMSSN900
                        valorICMSST = calculeVICMSST(icmsTO.getVBCST().toString(), icmsTO.getPICMSST().toString(), icmsTO.getVICMS().toString());
                        break;
                    case 3: //ICMSSN201
                    case 4: //ICMSSN202
                    case 5: //ICMSSN202/203
                        valorICMSST = calculeVICMSST(icmsTO.getVBCST().toString(), icmsTO.getPICMSST().toString(), null);
                        break;
                }
            }
            icmsTO.setVICMSST(numberHelper.getBigDecimal(valorICMSST));
        }
    }

    /**
     * Calcula o valor do ICMS ST.
     */
    private String calculeVICMSST(String vBCST, String pICMSST, String vICMS) {
        String vICMSSTMinuendo = numberHelper.multiplicar(vBCST, numberHelper.getPercentual(pICMSST));
        if ((StringHelper.isBlankOrNull(vICMS)) || (StringHelper.isBlankOrNull(vICMSSTMinuendo))) {
            return vICMSSTMinuendo;
        }
        BigDecimal valorICMSST = numberHelper.subtrair(numberHelper.getBigDecimal(vICMSSTMinuendo), new String[]{vICMS});
        if (valorICMSST.doubleValue() < 0.0D) {
            valorICMSST = BigDecimal.ZERO;
        }
        return numberHelper.toString(valorICMSST, -1, -1, 2, 2);
    }

    /**
     * Calcula o valor do PIS para o objeto PisTO.
     * <p>Para cálculo do "<b>vPIS</b>" para o tipo PISAliq a fórmula de cálculo é:
     * <br><blockquote><i><b>vPIS = vBC x (pPIS / 100)</b></i></blockquote>
     * <p>Para o tipo PISQtde a fórmule é:
     * <br><blockquote><i><b>vPIS = qBCProd x vAliqProd</b></i></blockquote>
     * <br>Para o tipos PISOutr a fórmula é:
     * <br><blockquote><i><b>vPIS = vBC x (pPIS / 100) --> Quando vBC e pPIS não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vPIS = qBCProd x vAliqProd --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVPIS(NotaFiscalTO notaFiscalTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscalTO.getListaProdutos()) {
            calculateVPIS(produtoNotaTO.getPIS());
        }
    }

    /**
     * Calcula o valor do PIS para o objeto PisTO.
     * <p>Para cálculo do "<b>vPIS</b>" para o tipo PISAliq a fórmula de cálculo é:
     * <br><blockquote><i><b>vPIS = vBC x (pPIS / 100)</b></i></blockquote>
     * <p>Para o tipo PISQtde a fórmule é:
     * <br><blockquote><i><b>vPIS = qBCProd x vAliqProd</b></i></blockquote>
     * <br>Para o tipos PISOutr a fórmula é:
     * <br><blockquote><i><b>vPIS = vBC x (pPIS / 100) --> Quando vBC e pPIS não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vPIS = qBCProd x vAliqProd --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVPIS(PisTO pisTO) {
        if (pisTO != null) {
            String valorPIS = null;
            if (pisTO.getCSTEnum() != null) {
                switch (pisTO.getCSTEnum().ordinal()) {
                    case 0: //CST = 01
                    case 1: //CST = 02
                        valorPIS = numberHelper.multiplicar(pisTO.getVBC().toString(), numberHelper.getPercentual(pisTO.getPPIS().toString()));
                        break;
                    case 2: //CST = 03
                        valorPIS = numberHelper.multiplicar(pisTO.getQBCProd().toString(), pisTO.getVAliqProd().toString(), RoundingMode.UP);
                        break;
                    case 8: //CST = 49
                    case 9: //CST = 50
                    case 10: //CST = 51
                    case 11: //CST = 52
                    case 12: //CST = 53
                    case 13: //CST = 54
                    case 14: //CST = 55
                    case 15: //CST = 56
                    case 16: //CST = 60
                    case 17: //CST = 61
                    case 18: //CST = 62
                    case 19: //CST = 63
                    case 20: //CST = 64
                    case 21: //CST = 65
                    case 22: //CST = 66
                    case 23: //CST = 67
                    case 24: //CST = 70
                    case 25: //CST = 71
                    case 26: //CST = 72
                    case 27: //CST = 73
                    case 28: //CST = 74
                    case 29: //CST = 75
                    case 30: //CST = 98
                    case 31: //CST = 99
                        if ((pisTO.getVBC() != null) && (pisTO.getPPIS() != null)) {
                            valorPIS = numberHelper.multiplicar(pisTO.getVBC().toString(), numberHelper.getPercentual(pisTO.getPPIS().toString()));
                        } else {
                            valorPIS = numberHelper.multiplicar(pisTO.getQBCProd().toString(), pisTO.getVAliqProd().toString(), RoundingMode.HALF_UP);
                        }
                        break;
                }
            }
            pisTO.setVPIS(numberHelper.getBigDecimal(valorPIS));
        }
    }

    /**
     * Calcula o valor do PIS ST para o objeto PisSTTO.
     * <p>Para cálculo do "<b>vPIS</b>" a fórmula de cálculo é:
     * <blockquote><i><b>vPIS = vBC x (pPIS / 100) --> Quando vBC e pPIS não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vPIS = qBCProd x vAliqProd --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVPISST(NotaFiscalTO notaFiscalTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscalTO.getListaProdutos()) {
            calculateVPISST(produtoNotaTO.getPISST());
        }
    }

    /**
     * Calcula o valor do PIS ST para o objeto PisSTTO.
     * <p>Para cálculo do "<b>vPIS</b>" a fórmula de cálculo é:
     * <blockquote><i><b>vPIS = vBC x (pPIS / 100) --> Quando vBC e pPIS não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vPIS = qBCProd x vAliqProd --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVPISST(PisSTTO pisSTTO) {
        if (pisSTTO != null) {
            String valorPISST = null;
            if ((pisSTTO.getVBC() != null) && (pisSTTO.getPPIS() != null)) {
                valorPISST = numberHelper.multiplicar(pisSTTO.getVBC().toString(), numberHelper.getPercentual(pisSTTO.getPPIS().toString()));
            } else {
                valorPISST = numberHelper.multiplicar(pisSTTO.getQBCProd().toString(), pisSTTO.getVAliqProd().toString(), RoundingMode.UP);
            }
            pisSTTO.setVPIS(numberHelper.getBigDecimal(valorPISST));
        }
    }

    /**
     * Calcula o valor da COFINS para o objeto CofinsTO.
     * <p>Para cálculo do "<b>vCOFINS</b>" para o tipo COFINSAliq a fórmula de cálculo é:
     * <br><blockquote><i><b>vCOFINS = vBC x (pCOFINS / 100)</b></i></blockquote>
     * <p>Para o tipo COFINSQtde a fórmule é:
     * <br><blockquote><i><b>vCOFINS = qBCProd x vAliqProd</b></i></blockquote>
     * <br>Para o tipos COFINSOutr a fórmula é:
     * <br><blockquote><i><b>vCOFINS = vBC x (pCOFINS / 100) --> Quando vBC e pCOFINS não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vCOFINS = qBCProd x vAliqProd --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVCOFINS(NotaFiscalTO notaFiscalTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscalTO.getListaProdutos()) {
            calculateVCOFINS(produtoNotaTO.getCOFINS());
        }
    }

    /**
     * Calcula o valor da COFINS para o objeto CofinsTO.
     * <p>Para cálculo do "<b>vCOFINS</b>" para o tipo COFINSAliq a fórmula de cálculo é:
     * <br><blockquote><i><b>vCOFINS = vBC x (pCOFINS / 100)</b></i></blockquote>
     * <p>Para o tipo COFINSQtde a fórmule é:
     * <br><blockquote><i><b>vCOFINS = qBCProd x vAliqProd</b></i></blockquote>
     * <br>Para o tipos COFINSOutr a fórmula é:
     * <br><blockquote><i><b>vCOFINS = vBC x (pCOFINS / 100) --> Quando vBC e pCOFINS não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vCOFINS = qBCProd x vAliqProd --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVCOFINS(CofinsTO cofinsTO) {
        if (cofinsTO != null) {
            String valorCOFINS = null;
            if (cofinsTO.getCSTEnum() != null) {
                switch (cofinsTO.getCSTEnum().ordinal()) {
                    case 0: //CST = 01
                    case 1: //CST = 02
                        valorCOFINS = numberHelper.multiplicar(cofinsTO.getVBC().toString(), numberHelper.getPercentual(cofinsTO.getPCOFINS().toString()));
                        break;
                    case 2: //CST = 03
                        valorCOFINS = numberHelper.multiplicar(cofinsTO.getQBCProd().toString(), cofinsTO.getVAliqProd().toString(), RoundingMode.UP);
                        break;
                    case 8: //CST = 49
                    case 9: //CST = 50
                    case 10: //CST = 51
                    case 11: //CST = 52
                    case 12: //CST = 53
                    case 13: //CST = 54
                    case 14: //CST = 55
                    case 15: //CST = 56
                    case 16: //CST = 60
                    case 17: //CST = 61
                    case 18: //CST = 62
                    case 19: //CST = 63
                    case 20: //CST = 64
                    case 21: //CST = 65
                    case 22: //CST = 66
                    case 23: //CST = 67
                    case 24: //CST = 70
                    case 25: //CST = 71
                    case 26: //CST = 72
                    case 27: //CST = 73
                    case 28: //CST = 74
                    case 29: //CST = 75
                    case 30: //CST = 98
                    case 31: //CST = 99
                        if ((cofinsTO.getVBC() != null) && (cofinsTO.getPCOFINS() != null)) {
                            valorCOFINS = numberHelper.multiplicar(cofinsTO.getVBC().toString(), numberHelper.getPercentual(cofinsTO.getPCOFINS().toString()));
                        } else {
                            valorCOFINS = numberHelper.multiplicar(cofinsTO.getQBCProd().toString(), cofinsTO.getVAliqProd().toString(), RoundingMode.UP);
                        }
                        break;
                }
            }
            cofinsTO.setVCOFINS(numberHelper.getBigDecimal(valorCOFINS));
        }
    }

    /**
     * Calcula o valor do COFINS ST para o objeto CofinsSTTO.
     * <p>Para cálculo do "<b>vCOFINS</b>" a fórmula de cálculo é:
     * <blockquote><i><b>vCOFINS = vBC x (pCOFINS / 100) --> Quando vBC e pCOFINS não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vCOFINS = qBCProd x vAliqProd --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVCOFINSST(NotaFiscalTO notaFiscalTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscalTO.getListaProdutos()) {
            calculateVCOFINSST(produtoNotaTO.getCOFINSST());
        }
    }

    /**
     * Calcula o valor do COFINS ST para o objeto CofinsSTTO.
     * <p>Para cálculo do "<b>vCOFINS</b>" a fórmula de cálculo é:
     * <blockquote><i><b>vCOFINS = vBC x (pCOFINS / 100) --> Quando vBC e pCOFINS não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vCOFINS = qBCProd x vAliqProd --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVCOFINSST(CofinsSTTO cofinsSTTO) {
        if (cofinsSTTO != null) {
            String valorCOFINS = null;
            if ((cofinsSTTO.getVBC() != null) && (cofinsSTTO.getPCOFINS() != null)) {
                valorCOFINS = numberHelper.multiplicar(cofinsSTTO.getVBC().toString(), numberHelper.getPercentual(cofinsSTTO.getPCOFINS().toString()));
            } else {
                valorCOFINS = numberHelper.multiplicar(cofinsSTTO.getQBCProd().toString(), cofinsSTTO.getVAliqProd().toString(), RoundingMode.UP);
            }
            cofinsSTTO.setVCOFINS(numberHelper.getBigDecimal(valorCOFINS));
        }
    }

    /**
     * Calcula o valor do IPI para o objeto IpiTO.
     * <p>Para cálculo do "<b>vIPI</b>" para o tipo IPITrib a fórmula de cálculo é:
     * <blockquote><i><b>vIPI = vBC x (pIPI / 100) --> Quando vBC e pIPI não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vIPI = qUnid x vUnid --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVIPI(NotaFiscalTO notaFiscalTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscalTO.getListaProdutos()) {
            calculateVIPI(produtoNotaTO.getIPI());
        }
    }

    /**
     * Calcula o valor do IPI para o objeto IpiTO.
     * <p>Para cálculo do "<b>vIPI</b>" para o tipo IPITrib a fórmula de cálculo é:
     * <blockquote><i><b>vIPI = vBC x (pIPI / 100) --> Quando vBC e pIPI não forem nulos</b></i></blockquote>
     * <blockquote><i><b>vIPI = qUnid x vUnid --> Nos demais casos</b></i></blockquote>
     */
    public void calculateVIPI(IpiTO ipiTO) {
        if (ipiTO != null) {
            String valorIPI = null;
            if (ipiTO.getCSTEnum() != null) {
                switch (ipiTO.getCSTEnum().ordinal()) {
                    case 0: //IPI 00
                    case 6: //IPI 49
                    case 7: //IPI 50
                    case 13: //IPI 99
                        if ((ipiTO.getVBC() != null) && (ipiTO.getPIPI() != null)) {
                            valorIPI = numberHelper.multiplicar(ipiTO.getVBC().toString(), numberHelper.getPercentual(ipiTO.getPIPI().toString()));
                        } else {
                            valorIPI = numberHelper.multiplicar(ipiTO.getQUnid().toString(), ipiTO.getVUnid().toString(), RoundingMode.UP);
                        }
                        break;
                }
            }
            ipiTO.setVIPI(numberHelper.getBigDecimal(valorIPI));
        }
    }

    /**
     * Calcula o valor do ISSQN para o objeto IssqnTO.
     * <p>Para cálculo do "<b>vISSQN</b>" a fórmula de cálculo é:
     * <blockquote><i><b>vISSQN = vBC x (pAliq / 100)</b></i></blockquote>
     */
    public void calculateVISSQN(NotaFiscalTO notaFiscalTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscalTO.getListaProdutos()) {
            calculateVISSQN(produtoNotaTO.getISSQN());
        }
    }

    /**
     * Calcula o valor do ISSQN para o objeto IssqnTO.
     * <p>Para cálculo do "<b>vISSQN</b>" a fórmula de cálculo é:
     * <blockquote><i><b>vISSQN = vBC x (pAliq / 100)</b></i></blockquote>
     */
    public void calculateVISSQN(IssqnTO issqnTO) {
        if (issqnTO != null) {
            String valorISSQN = numberHelper.multiplicar(issqnTO.getVBC().toString(), numberHelper.getPercentual(issqnTO.getVAliq().toString()));
            issqnTO.setVISSQN(numberHelper.getBigDecimal(valorISSQN));
        }
    }

    /**
     * Calcula e atribui os valores totais da NF-e.
     */
    public void calculateTotalNotaFiscal(NotaFiscalTO notaFiscalTO) throws HSCommonException {
        //Inicializa as variáveis zeradas
        initTotais();
        try {
            if ((notaFiscalTO != null) && (notaFiscalTO.getListaProdutos() != null)) {
                //Obtém o xml da nota fiscal
                NFeDocument doc = DocumentHelper.createNFeDocument(notaFiscalTO.getXmlString());
                TNFe.InfNFe.Total total = doc.getNFe().getInfNFe().getTotal() != null ? doc.getNFe().getInfNFe().getTotal() : doc.getNFe().getInfNFe().addNewTotal();
                TNFe.InfNFe.Total.ICMSTot icmsTotal = total.getICMSTot() != null ? total.getICMSTot() : total.addNewICMSTot();
                //Soma os impostos da NF-e
                somarImpostos(notaFiscalTO);
                //Obtém o total da NF-e
                BigDecimal totalNFe = somarTotal();
                //Formatação dos dados
                DecimalFormat totalFormat = new DecimalFormat("###.##");
                DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
                symbols.setDecimalSeparator('.');
                totalFormat.setDecimalFormatSymbols(symbols);
                totalFormat.setMinimumFractionDigits(2);
                totalFormat.setMaximumIntegerDigits(13);
                totalFormat.setParseBigDecimal(true);
                //insere o total dos dados
                icmsTotal.setVBC(totalFormat.format(vBCICMS.doubleValue()));
                icmsTotal.setVICMS(totalFormat.format(vICMS.doubleValue()));
                icmsTotal.setVBCST(totalFormat.format(vICMSBCST.doubleValue()));
                icmsTotal.setVST(totalFormat.format(vICMSST.doubleValue()));
                icmsTotal.setVProd(totalFormat.format(vProd.doubleValue()));
                icmsTotal.setVFrete(totalFormat.format(vFrete.doubleValue()));
                icmsTotal.setVSeg(totalFormat.format(vSeg.doubleValue()));
                icmsTotal.setVDesc(totalFormat.format(vDesc.doubleValue()));
                icmsTotal.setVII(totalFormat.format(vII.doubleValue()));
                icmsTotal.setVIPI(totalFormat.format(vIPI.doubleValue()));
                icmsTotal.setVPIS(totalFormat.format(vPIS.doubleValue()));
                icmsTotal.setVCOFINS(totalFormat.format(vCOFINS.doubleValue()));
                icmsTotal.setVOutro(totalFormat.format(vOutro.doubleValue()));
                icmsTotal.setVNF(totalFormat.format(totalNFe.doubleValue()));
                //Verifica o nó ISSQNTotal
                TNFe.InfNFe.Total.ISSQNtot issqnTotal = total.getISSQNtot();
                if (vBCISSQN.doubleValue() != 0.0D) {
                    if (issqnTotal == null) {
                        issqnTotal = total.addNewISSQNtot();
                    }
                    issqnTotal.setVBC(totalFormat.format(vBCISSQN.doubleValue()));
                } else if (issqnTotal != null) {
                    issqnTotal.unsetVBC();
                }
                if (vISSQN.doubleValue() != 0.0D) {
                    if (issqnTotal == null) {
                        issqnTotal = total.addNewISSQNtot();
                    }
                    issqnTotal.setVISS(totalFormat.format(vISSQN.doubleValue()));
                } else if (issqnTotal != null) {
                    issqnTotal.unsetVISS();
                }
                //Configura o novo Xml
                notaFiscalTO.setXmlString(DocumentHelper.createDocumentString(doc, true));
            }
        } catch (Exception ex) {
            throw new HSCommonException(ex.getMessage(), ex);
        }
    }

    /**
     * Soma os valores dos impostos.
     */
    private void somarImpostos(NotaFiscalTO notaFiscalTO) {
        for (ProdutoNotaTO produtoNotaTO : notaFiscalTO.getListaProdutos()) {
            somarICMS(produtoNotaTO.getICMS());
            somarIPI(produtoNotaTO.getIPI());
            somarPIS(produtoNotaTO.getPIS());
            somarII(produtoNotaTO.getII());
            somarCOFINS(produtoNotaTO.getCOFINS());
            somarISSQN(produtoNotaTO.getISSQN());
            somarProdutos(produtoNotaTO.getProduto());
        }
    }

    /**
     * Soma os valores do ICMS.
     */
    private void somarICMS(IcmsTO icmsTO) {
        if (icmsTO != null) {
            if (icmsTO.getCSTEnum() != null) {
                switch (icmsTO.getCSTEnum().ordinal()) {
                    case 0: //ICMS00
                    case 2: //ICMS20
                        vBCICMS = numberHelper.somar(vBCICMS, new String[]{icmsTO.getVBC().toString()});
                        vICMS = numberHelper.somar(vICMS, new String[]{icmsTO.getVICMS().toString()});
                        break;
                    case 1: //ICMS10
                    case 9: //ICMS70
                        vBCICMS = numberHelper.somar(vBCICMS, new String[]{icmsTO.getVBC().toString()});
                        vICMS = numberHelper.somar(vICMS, new String[]{icmsTO.getVICMS().toString()});
                        vICMSBCST = numberHelper.somar(vICMSBCST, new String[]{icmsTO.getVBCST().toString()});
                        vICMSST = numberHelper.somar(vICMSST, new String[]{icmsTO.getVICMSST().toString()});
                        break;
                    case 3: //ICMS30
                        vICMSBCST = numberHelper.somar(vICMSBCST, new String[]{icmsTO.getVBCST().toString()});
                        vICMSST = numberHelper.somar(vICMSST, new String[]{icmsTO.getVICMSST().toString()});
                        break;
                    case 10: //ICMS90
                    case 11: //ICMSPart 10
                    case 12: //ICMSPart 90
                        if (icmsTO.getVBC() != null) {
                            vBCICMS = numberHelper.somar(vBCICMS, new String[]{icmsTO.getVBC().toString()});
                            vICMS = numberHelper.somar(vICMS, new String[]{icmsTO.getVICMS().toString()});
                        }
                        if (icmsTO.getVBCST() != null) {
                            vICMSBCST = numberHelper.somar(vICMSBCST, new String[]{icmsTO.getVBCST().toString()});
                            vICMSST = numberHelper.somar(vICMSST, new String[]{icmsTO.getVICMSST().toString()});
                        }
                        break;
                }
            } else if (icmsTO.getCSOSNEnum() != null) {
                switch (icmsTO.getCSOSNEnum().ordinal()) {
                    case 3: //ICMSSN201
                    case 4: //ICMSSN202
                    case 5: //ICMS203/203
                        vICMSBCST = numberHelper.somar(vICMSBCST, new String[]{icmsTO.getVBCST().toString()});
                        vICMSST = numberHelper.somar(vICMSST, new String[]{icmsTO.getVICMSST().toString()});
                        break;
                    case 9: //ICMSSN900
                        if (icmsTO.getVBC() != null) {
                            vBCICMS = numberHelper.somar(vBCICMS, new String[]{icmsTO.getVBC().toString()});
                            vICMS = numberHelper.somar(vICMS, new String[]{icmsTO.getVICMS().toString()});
                        }
                        if (icmsTO.getVBCST() != null) {
                            vICMSBCST = numberHelper.somar(vICMSBCST, new String[]{icmsTO.getVBCST().toString()});
                            vICMSST = numberHelper.somar(vICMSST, new String[]{icmsTO.getVICMSST().toString()});
                        }
                        break;
                }
            }
        }
    }

    /**
     * Soma os valores do IPI.
     */
    private void somarIPI(IpiTO ipiTO) {
        if ((ipiTO != null) && (ipiTO.getCSTEnum() != null)) {
            switch (ipiTO.getCSTEnum().ordinal()) {
                case 0: //CST = 00
                case 6: //CST = 49
                case 7: //CST = 50
                case 13: //CST = 99
                    vIPI = numberHelper.somar(vIPI, new String[]{ipiTO.getVIPI().toString()});
                    break;
            }
        }
    }

    /**
     * Soma os valores do PIS.
     */
    private void somarPIS(PisTO pisTO) {
        if ((pisTO != null) && (pisTO.getCSTEnum() != null)) {
            switch (pisTO.getCSTEnum().ordinal()) {
                case 0: //CST = 01
                case 1: //CST = 02
                case 2: //CST = 03
                case 8: //CST = 49
                case 9: //CST = 50
                case 10: //CST = 51
                case 11: //CST = 52
                case 12: //CST = 53
                case 13: //CST = 54
                case 14: //CST = 55
                case 15: //CST = 56
                case 16: //CST = 60
                case 17: //CST = 61
                case 18: //CST = 62
                case 19: //CST = 63
                case 20: //CST = 64
                case 21: //CST = 65
                case 22: //CST = 66
                case 23: //CST = 67
                case 24: //CST = 70
                case 25: //CST = 71
                case 26: //CST = 72
                case 27: //CST = 73
                case 28: //CST = 74
                case 29: //CST = 75
                case 30: //CST = 98
                case 31: //CST = 99
                    vPIS = numberHelper.somar(vPIS, new String[]{pisTO.getVPIS().toString()});
                    break;
            }
        }
    }

    /**
     * Soma os valores do II.
     */
    private void somarII(IITO iiTO) {
        if (iiTO != null) {
            vII = numberHelper.somar(vII, new String[]{iiTO.getVII().toString()});
        }
    }

    /**
     * Soma os valores do COFINS.
     */
    private void somarCOFINS(CofinsTO cofinsTO) {
        if ((cofinsTO != null) && (cofinsTO.getCSTEnum() != null)) {
            switch (cofinsTO.getCSTEnum().ordinal()) {
                case 0: //CST = 01
                case 1: //CST = 02
                case 2: //CST = 03
                case 8: //CST = 49
                case 9: //CST = 50
                case 10: //CST = 51
                case 11: //CST = 52
                case 12: //CST = 53
                case 13: //CST = 54
                case 14: //CST = 55
                case 15: //CST = 56
                case 16: //CST = 60
                case 17: //CST = 61
                case 18: //CST = 62
                case 19: //CST = 63
                case 20: //CST = 64
                case 21: //CST = 65
                case 22: //CST = 66
                case 23: //CST = 67
                case 24: //CST = 70
                case 25: //CST = 71
                case 26: //CST = 72
                case 27: //CST = 73
                case 28: //CST = 74
                case 29: //CST = 75
                case 30: //CST = 98
                case 31: //CST = 99
                    vCOFINS = numberHelper.somar(vCOFINS, new String[]{cofinsTO.getVCOFINS().toString()});
                    break;
            }
        }
    }

    /**
     * Soma os valores do ISSQN.
     */
    private void somarISSQN(IssqnTO issqnTO) {
        if (issqnTO != null) {
            vBCISSQN = numberHelper.somar(vBCISSQN, new String[]{issqnTO.getVBC().toString()});
            vISSQN = numberHelper.somar(vISSQN, new String[]{issqnTO.getVISSQN().toString()});
        }
    }

    /**
     * Soma os valores do produto.
     */
    private void somarProdutos(ProdutoTO produtoTO) {
        if (produtoTO != null) {
            if (IndTotEnum.X_1.equals(produtoTO.getIndTotEnum())) {
                vProd = numberHelper.somar(vProd, new String[]{produtoTO.getVProd().toString()});
            }
            if (produtoTO.getVFrete() != null) {
                vFrete = numberHelper.somar(vFrete, new String[]{produtoTO.getVFrete().toString()});
            }
            if (produtoTO.getVSeg() != null) {
                vSeg = numberHelper.somar(vSeg, new String[]{produtoTO.getVSeg().toString()});
            }
            if (produtoTO.getVDesc() != null) {
                vDesc = numberHelper.somar(vDesc, new String[]{produtoTO.getVDesc().toString()});
            }
            if (produtoTO.getVOutro() != null) {
                vOutro = numberHelper.somar(vOutro, new String[]{produtoTO.getVOutro().toString()});
            }
        }
    }

    /**
     * Obtém o valor total da NF-e.
     */
    private BigDecimal somarTotal() {
        BigDecimal total = vProd.subtract(vDesc).add(vICMSST).add(vFrete).add(vSeg).add(vOutro).add(vII).add(vIPI);
        return total;
    }

    /**
     * Calcula e atribui a chave de acesso da NF-e.
     */
    public void setID(NFeDocument doc, NotaFiscalTO notaFiscalTO) throws HSCommonException {
        try {
            doc.getNFe().getInfNFe().setId(HSConstants.ID_NFE + notaFiscalTO.getChaveAcesso());
            doc.getNFe().getInfNFe().getIde().setCDV(notaFiscalTO.getCDV());
            notaFiscalTO.setXmlString(DocumentHelper.createDocumentString(doc, true));
        } catch (Exception ex) {
            throw new HSCommonException(ex.getMessage(), ex);
        }
    }
}
